import { Fragment, useRef, useState } from 'react';
import {
  StyledLayout,
  CopyButton,
  CodeEditor,
  Button,
  Wrapper,
  ErrorLayout,
  ResultCode,
  Spacing,
} from '@wcj/tools-react-components';
import { html } from '@codemirror/lang-html';
import { minify, Options } from 'html-minifier';
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import prettier from 'prettier';
import parserHTML from 'prettier/parser-html';
import * as sample from './sample';

type Type = 'formatter' | 'minify';

const Info = styled.span`
  color: var(--color-fg-subtle);
`;

const Danger = styled.span`
  color: red;
`;

const defaultOptions: Options = {
  collapseBooleanAttributes: true,
  collapseWhitespace: true,
  decodeEntities: true,
  html5: true,
  minifyCSS: true,
  processConditionalComments: true,
  minifyJS: true,
  processScripts: ['text/html'],
  removeAttributeQuotes: true,
  removeComments: true,
  removeEmptyAttributes: true,
  removeOptionalTags: true,
  removeRedundantAttributes: true,
  removeScriptTypeAttributes: true,
  removeStyleLinkTypeAttributes: true,
  removeTagWhitespace: true,
  sortAttributes: true,
  sortClassName: true,
  trimCustomFragments: true,
  useShortDoctype: true,
};

const allOptions: Options = {
  ...defaultOptions,
  caseSensitive: true,
  collapseInlineTagWhitespace: true,
  conservativeCollapse: true,
  includeAutoGeneratedTags: true,
  keepClosingSlash: true,
  preserveLineBreaks: true,
  preventAttributesEscaping: true,
  removeEmptyElements: true,
};

export default function HTMLFormatter() {
  const { t } = useTranslation('html-formatter');
  const editor = useRef<ReactCodeMirrorRef>(null);
  const [type, setType] = useState<Type>('minify');
  const [value, setValue] = useState('');
  const [options, setOptions] = useState<Options>(defaultOptions);
  const [error, setError] = useState('');
  const [tabWidth, setTabWidth] = useState(2);
  const handleFormat = () => {
    try {
      setError('');
      setValue(prettier.format(value, { parser: 'html', tabWidth: tabWidth, printWidth: 120, plugins: [parserHTML] }));
    } catch (error) {
      if (error instanceof Error) {
        setError(error.message);
      }
    }
  };
  const handleMinify = () => {
    try {
      setError('');
      const val = minify(value, { ...options });
      setValue(val || '');
    } catch (error) {
      if (error instanceof Error) {
        console.log('error', error);
        setError(error.message);
      }
    }
  };
  return (
    <Wrapper>
      <StyledLayout
        title="HTML Formatter/Minify"
        extra={
          <Fragment>
            {value && (
              <Fragment>
                {type === 'formatter' && <Button onClick={() => handleFormat()}>Formatter</Button>}
                {type === 'minify' && <Button onClick={() => handleMinify()}>Minify</Button>}
              </Fragment>
            )}
            {value && (
              <select value={type} onChange={(evn) => setType(evn.target.value as Type)}>
                <option value="formatter">Formatter</option>
                <option value="minify">Minify</option>
              </select>
            )}
            {value && type === 'formatter' && (
              <select onChange={(evn) => setTabWidth(Number(evn.target.value))}>
                <option value={2}>2 Tab Space</option>
                <option value={3}>3 Tab Space</option>
                <option value={4}>4 Tab Space</option>
                <option value={5}>5 Tab Space</option>
              </select>
            )}
            {value && <CopyButton value={value} />}
            <Button
              onClick={() => {
                editor.current?.view?.focus();
                if (type === 'formatter') {
                  setValue(sample.html);
                }
                if (type === 'minify') {
                  setValue(sample.val);
                }
              }}
            >
              Sample
            </Button>
          </Fragment>
        }
      >
        <CodeEditor
          value={value}
          ref={editor}
          height="calc(100vh - 87px)"
          extensions={[html()]}
          onChange={(value) => {
            setValue(value);
          }}
        />
      </StyledLayout>
      {type === 'minify' && (
        <StyledLayout
          title={t('Options').toString()}
          style={{ maxWidth: 420 }}
          extra={
            <Fragment>
              <Button onClick={() => setOptions(defaultOptions)}>Reset</Button>
              <Button onClick={() => setOptions(allOptions)}>All</Button>
              <Button onClick={() => setOptions({})}>None</Button>
            </Fragment>
          }
        >
          <ResultCode codeProps={{ style: { height: 'calc(100vh - 87px)', overflow: 'auto', margin: 0 } }}>
            <Spacing>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.caseSensitive}
                    onChange={({ target }) => setOptions({ ...options, caseSensitive: target.checked })}
                  />
                  {t('Case-sensitive').toString()}{' '}
                  <Info>{t('Treat attributes in case sensitive manner (useful for custom HTML tags)').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.collapseBooleanAttributes}
                    onChange={({ target }) => setOptions({ ...options, collapseBooleanAttributes: target.checked })}
                  />
                  {t('Collapse boolean attributes').toString()}{' '}
                  <Info>{t('Omit attribute values from boolean attributes').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.collapseInlineTagWhitespace}
                    onChange={({ target }) => setOptions({ ...options, collapseInlineTagWhitespace: target.checked })}
                  />
                  <Danger>{t('Collapse inline tag whitespace').toString()}</Danger>{' '}
                  <Info>
                    {t(
                      `Don't leave any spaces between display:inline; elements when collapsing. Must be used in conjunction with collapseWhitespace=true`,
                    ).toString()}
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.collapseWhitespace}
                    onChange={({ target }) => setOptions({ ...options, collapseWhitespace: target.checked })}
                  />
                  {t('Collapse whitespace').toString()}{' '}
                  <Info>{t(`Collapse white space that contributes to text nodes in a document tree`).toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.conservativeCollapse}
                    onChange={({ target }) => setOptions({ ...options, conservativeCollapse: target.checked })}
                  />
                  {t('Conservative collapse').toString()}{' '}
                  <Info>
                    {t(
                      `Always collapse to 1 space (never remove it entirely). Must be used in conjunction with collapseWhitespace=true`,
                    ).toString()}
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.decodeEntities}
                    onChange={({ target }) => setOptions({ ...options, decodeEntities: target.checked })}
                  />
                  {t('Decode Entity Characters').toString()}{' '}
                  <Info>{t('Use direct Unicode characters whenever possible').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.html5}
                    onChange={({ target }) => setOptions({ ...options, html5: target.checked })}
                  />
                  HTML5 <Info>{t('Parse input according to HTML5 specifications').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.includeAutoGeneratedTags}
                    onChange={({ target }) => setOptions({ ...options, includeAutoGeneratedTags: target.checked })}
                  />
                  {t('Include auto-generated tags').toString()}{' '}
                  <Info>{t('Insert tags generated by HTML parser').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.keepClosingSlash}
                    onChange={({ target }) => setOptions({ ...options, keepClosingSlash: target.checked })}
                  />
                  {t('Keep closing slash').toString()}{' '}
                  <Info>{t('Keep the trailing slash on singleton elements').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  Max.line length
                  <input
                    type="number"
                    value={options.maxLineLength}
                    onChange={({ target }) =>
                      setOptions({ ...options, ...(target.value ? { maxLineLength: Number(target.value) } : {}) })
                    }
                  />
                  <Info>
                    {t(
                      'Specify a maximum line length. Compressed output will be split by newlines at valid HTML split-points',
                    ).toString()}
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.minifyCSS}
                    onChange={({ target }) => setOptions({ ...options, minifyCSS: target.checked })}
                  />
                  {t('Minify CSS').toString()}{' '}
                  <Info>
                    {t('Minify CSS in style elements and style attributes (uses').toString()}{' '}
                    <a href="https://github.com/jakubpawlowicz/clean-css" target="_blank">
                      clean-css
                    </a>
                    )
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.minifyJS}
                    onChange={({ target }) => setOptions({ ...options, minifyJS: target.checked })}
                  />
                  {t('Minify JavaScript').toString()}{' '}
                  <Info>
                    {t('Minify JavaScript in script elements and event attributes (uses').toString()}{' '}
                    <a href="https://github.com/jaywcjlove/uglify-js-export" target="_blank">
                      UglifyJS
                    </a>
                    )
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  {t('Minify URLs').toString()}{' '}
                  <input
                    type="number"
                    value={options.minifyURLs as string}
                    onChange={({ target }) =>
                      setOptions({ ...options, ...(target.value ? ({ minifyURLs: target.value } as any) : {}) })
                    }
                  />
                  <Info>
                    Minify URLs in various attributes (uses{' '}
                    <a href="https://github.com/stevenvachon/relateurl" target="_blank">
                      relateurl
                    </a>
                    )
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.preserveLineBreaks}
                    onChange={({ target }) => setOptions({ ...options, preserveLineBreaks: target.checked })}
                  />
                  {t('Preserve line-breaks').toString()}
                  <Info>
                    Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a
                    line break. Must be used in conjunction with collapseWhitespace=true
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.preventAttributesEscaping}
                    onChange={({ target }) => setOptions({ ...options, preventAttributesEscaping: target.checked })}
                  />
                  <Danger>{t('Prevent attributes escaping').toString()}</Danger>{' '}
                  <Info>Prevents the escaping of the values of attributes</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.processConditionalComments}
                    onChange={({ target }) => setOptions({ ...options, processConditionalComments: target.checked })}
                  />
                  {t('Process conditional comments').toString()}{' '}
                  <Info>Process contents of conditional comments through minifier</Info>
                </span>
              </label>
              <label>
                <span>
                  {t('Process scripts').toString()}{' '}
                  <input
                    type="text"
                    value={options.processScripts || ''}
                    onChange={({ target }) =>
                      setOptions({ ...options, ...(target.value ? { processScripts: target.value.split(',') } : {}) })
                    }
                  />
                  <Info>
                    Comma-delimited string corresponding to types of script elements to process through minifier (e.g.
                    text/ng-template, text/x-handlebars-template)
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  {t('Quote character').toString()}
                  <input
                    type="text"
                    value={options.quoteCharacter || ''}
                    onChange={({ target }) => setOptions({ ...options, quoteCharacter: target.value })}
                  />
                  <Info>Type of quote to use for attribute values (' or ")</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeAttributeQuotes}
                    onChange={({ target }) => setOptions({ ...options, removeAttributeQuotes: target.checked })}
                  />
                  {t('Remove attribute quotes').toString()} <Info>Remove quotes around attributes when possible</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeComments}
                    onChange={({ target }) => setOptions({ ...options, removeComments: target.checked })}
                  />
                  {t('Remove comments').toString()} <Info>Strip HTML comments</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeEmptyAttributes}
                    onChange={({ target }) => setOptions({ ...options, removeEmptyAttributes: target.checked })}
                  />
                  {t('Remove empty attributes').toString()}{' '}
                  <Info>Remove all attributes with whitespace-only values</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeEmptyElements}
                    onChange={({ target }) => setOptions({ ...options, removeEmptyElements: target.checked })}
                  />
                  <Danger>{t('Remove empty elements').toString()}</Danger>{' '}
                  <Info>Remove all elements with empty contents</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeOptionalTags}
                    onChange={({ target }) => setOptions({ ...options, removeOptionalTags: target.checked })}
                  />
                  {t('Remove optional tags').toString()}
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeRedundantAttributes}
                    onChange={({ target }) => setOptions({ ...options, removeRedundantAttributes: target.checked })}
                  />
                  {t('Remove redundant attributes').toString()}{' '}
                  <Info>{t('Remove attributes when value matches default.').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeScriptTypeAttributes}
                    onChange={({ target }) => setOptions({ ...options, removeScriptTypeAttributes: target.checked })}
                  />
                  {t('Remove script type attributes').toString()}{' '}
                  <Info>
                    Remove type="text/javascript" from script tags. Other type attribute values are left intact
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeStyleLinkTypeAttributes}
                    onChange={({ target }) => setOptions({ ...options, removeStyleLinkTypeAttributes: target.checked })}
                  />
                  {t('Remove style link type attributes').toString()}{' '}
                  <Info>
                    Remove type="text/css" from style and link tags. Other type attribute values are left intact
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.removeTagWhitespace}
                    onChange={({ target }) => setOptions({ ...options, removeTagWhitespace: target.checked })}
                  />
                  <Danger>{t('Remove tag whitespace').toString()}</Danger>{' '}
                  <Info>
                    Remove space between attributes whenever possible. Note that this will result in invalid HTML!
                  </Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.sortAttributes}
                    onChange={({ target }) => setOptions({ ...options, sortAttributes: target.checked })}
                  />
                  <Danger>{t('Sort attributes').toString()}</Danger>{' '}
                  <Info>{t('Sort attributes by frequency').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.sortClassName}
                    onChange={({ target }) => setOptions({ ...options, sortClassName: target.checked })}
                  />
                  <Danger>{t('Sort class name').toString()}</Danger>{' '}
                  <Info>{t('Sort style classes by frequency').toString()}</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.trimCustomFragments}
                    onChange={({ target }) => setOptions({ ...options, trimCustomFragments: target.checked })}
                  />
                  {t('Trim white space around custom fragments').toString()}{' '}
                  <Info>Trim white space around ignoreCustomFragments.</Info>
                </span>
              </label>
              <label>
                <span>
                  <input
                    type="checkbox"
                    checked={!!options.useShortDoctype}
                    onChange={({ target }) => setOptions({ ...options, useShortDoctype: target.checked })}
                  />
                  {t('Use short doctype').toString()} <Info>Replaces the doctype with the short (HTML5) doctype</Info>
                </span>
              </label>
            </Spacing>
          </ResultCode>
        </StyledLayout>
      )}
      {error && <ErrorLayout>{error}</ErrorLayout>}
    </Wrapper>
  );
}
